﻿using System;
using System.Security.Cryptography;
using System.Text;
using System.Web.Security;
using CodeStash.Common.Encryption;

namespace CodeStash.Services
{
    public class AccountMembershipService : IMembershipService
    {
        private readonly MembershipProvider provider;

        public AccountMembershipService()
        {
            provider = Membership.Provider;
        }

        public int MinPasswordLength
        {
            get
            {
                return provider.MinRequiredPasswordLength;
            }
        }

        public bool ValidateUser(string userName, string password)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");

            if (EncryptionHelper.EncryptionEnabled)
            {
                return provider.ValidateUser(userName, EncryptionHelper.GetEncryptedValue(password));
            }
            else
            {
                return provider.ValidateUser(userName, password);
            }
        }


        public Guid StringToGUID(string value)
        {
            // Create a new instance of the MD5CryptoServiceProvider object.
            MD5 md5Hasher = MD5.Create();
            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(value));
            return new Guid(data);
        }





        public MembershipCreateStatus CreateUser(string userName, string password, string email, string OpenID)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
            if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");

            MembershipCreateStatus status;
            Guid provKey;
            if (!string.IsNullOrWhiteSpace(OpenID))
            {
                provKey = StringToGUID(OpenID);
            }
            else
            {
                provKey = Guid.NewGuid();
            }

            if (EncryptionHelper.EncryptionEnabled)
            {
                provider.CreateUser(userName, EncryptionHelper.GetEncryptedValue(password),
                    EncryptionHelper.GetEncryptedValue(email), null, null, true, provKey, out status);
            }
            else
            {
                provider.CreateUser(userName, password, email, null, null, true, provKey, out status);
            }


            return status;
        }



        public Tuple<MembershipCreateStatus, String> CreateUser(string userName, string email, string OpenID)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
            if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");

            MembershipCreateStatus status;

            //ProvKey is actually used as UserId in database
            Guid provKey;
            if (!string.IsNullOrWhiteSpace(OpenID))
            {
                provKey = StringToGUID(OpenID);
            }
            else
            {
                provKey = Guid.NewGuid();
            }


            //Generate a password token value
            Guid passGuid = Guid.NewGuid();
            
            //Guid password = StringToGUID(passGuid.ToString());
            string encryptedPassToken = EncryptionHelper.GetEncryptedValue(passGuid.ToString());

            //TODO : At this point we should be able to share the Guid.NewGuid() with the user which we encrypt before we send to user
            //then with any request they make, they pass that password encrypted (which we decrypt here, and then Hash use MDF hash, and then lookup)
            //We should email them this encrypted Guid
            if (EncryptionHelper.EncryptionEnabled)
            {
                provider.CreateUser(userName, encryptedPassToken, EncryptionHelper.GetEncryptedValue(email), 
                    null, null, true, provKey, out status);
            }
            else
            {
                provider.CreateUser(userName, passGuid.ToString(), email, null, null, true, provKey, out status);
            }

            if (EncryptionHelper.EncryptionEnabled)
            {
                return Tuple.Create<MembershipCreateStatus, String>(status, encryptedPassToken);
            }
            else
            {
                return Tuple.Create<MembershipCreateStatus, String>(status, passGuid.ToString());
            }
        }



        public MembershipUser GetUserByOpenId(string OpenID)
        {
            return provider.GetUser(StringToGUID(OpenID), true);
        }

        public MembershipUser GetUserByUserName(string userName)
        {
            return provider.GetUser(userName, true);
        }




        public bool ChangePassword(string userName, string oldPassword, string newPassword)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
            if (String.IsNullOrEmpty(oldPassword)) throw new ArgumentException("Value cannot be null or empty.", "oldPassword");
            if (String.IsNullOrEmpty(newPassword)) throw new ArgumentException("Value cannot be null or empty.", "newPassword");

            // The underlying ChangePassword() will throw an exception rather
            // than return false in certain failure scenarios.
            try
            {
                MembershipUser currentUser = provider.GetUser(userName, true /* userIsOnline */);

                if (EncryptionHelper.EncryptionEnabled)
                {
                    return currentUser.ChangePassword(
                        EncryptionHelper.GetEncryptedValue(oldPassword), 
                        EncryptionHelper.GetEncryptedValue(newPassword));
                }
                else
                {
                    return currentUser.ChangePassword(oldPassword, newPassword);
                }
                
            }
            catch (ArgumentException)
            {
                return false;
            }
            catch (MembershipPasswordException)
            {
                return false;
            }
        }



    }

}